#include <memory.h>
#include <stdio.h>

FILE *f;
unsigned int bytes_per_sector, cluster_begin_lba, fat_begin_lba, lba_begin, number_of_fats, number_of_reserved_sectors, number_of_sectors, root_dir_first_cluster, sectors_per_cluster, sectors_per_fat;

struct file {
	unsigned char name[11];
	unsigned char longname[256];
	unsigned long first_cluster;
	unsigned long size;
};


#define IS_DIR(attr) ((attr) & 0x10)
#define IS_LONG_NAME(attr) ((attr) == 0x0F)


/* clusters begin their numbering from 2! */
size_t read_cluster(unsigned long n, char *buf)
{
	fseek(f, (cluster_begin_lba + (n - 2) * sectors_per_cluster) * bytes_per_sector, SEEK_SET);
	return fread(buf, sectors_per_cluster * bytes_per_sector, 1, f);
}

/* get_length: takes the position of the first cluster of the file
   and returns its length in clusters */
unsigned long get_length(unsigned long first_cluster)
{
	unsigned long curr = first_cluster;
	unsigned long length = 0;

	do {
		fseek(f, fat_begin_lba * bytes_per_sector + curr * 4, SEEK_SET);
		fread(&curr, 4, 1, f);
		length++;
	} while ((curr & 0x0FFFFF) < 0x0FFFF8);
	return length;
}

/* read_file: takes the position of the first cluster if the file,
   buffer to write and max data to read (should divide by 512)
   and reads the file from FAT32 filesystem */
void read_file(unsigned long first_cluster, unsigned char *buf, unsigned long max_size)
{
	unsigned long bytes_read = 0;
	unsigned long next_cluster = first_cluster;

	do {
		fseek(f, (cluster_begin_lba + (next_cluster - 2) * sectors_per_cluster) * bytes_per_sector, SEEK_SET);
		bytes_read += fread(buf + bytes_read, 1, sectors_per_cluster * bytes_per_sector, f);
		fseek(f, fat_begin_lba * bytes_per_sector + next_cluster * 4, SEEK_SET);
		fread(&next_cluster, 4, 1, f);
	} while ((next_cluster & 0x0FFFFF) < 0xFFFF8 && bytes_read < max_size);

	return;
}


struct file find_biggest(unsigned long dir_cluster)
{
	unsigned long i, j, k, tmp;
	unsigned long length = get_length(dir_cluster) * sectors_per_cluster * bytes_per_sector;
	unsigned char entry[32];
	unsigned char dir[length];
	struct file biggest_file, tmp_file;

	biggest_file.size = 0;
	biggest_file.longname[0] = 0;

	read_file(dir_cluster, dir, length);
	for (i = 0; i < length; i += 32) {
		memcpy(&entry, dir + i, 32);
		if (entry[0] == 0xE5 || IS_LONG_NAME(entry[0x0B])) {
			continue;
		}
		else if (entry[0] == 0) {
			break;
		}
		else if (IS_DIR(entry[0x0B]) && entry[0] != '.') {
			tmp = 0;
			tmp += entry[0x15];
			tmp <<= 8;
			tmp += entry[0x14];
			tmp <<= 8;
			tmp += entry[0x1B];
			tmp <<= 8;
			tmp += entry[0x1A];
			/*memcpy(&tmp, entry + 0x14 , 2);*/
			/*memcpy(&tmp + 2, entry + 0x1A, 2);*/
			tmp_file = find_biggest(tmp);
			if (tmp_file.size > biggest_file.size)
				biggest_file = tmp_file;
		}
		else {
			memcpy(&tmp, entry + 0x1C, 4);
			if (tmp > biggest_file.size) {
				/* if attr byte of prev entry is set to long name */
				if (i != 0 && IS_LONG_NAME(dir[i - 32 + 0x0B])) {
					unsigned short longname[sizeof(biggest_file.longname)];
					memset(longname, 0, sizeof(longname));
					j = 0;
					k = 0;
					do {
						j += 32;
						memcpy(longname + k, dir + i - j + 1, 10);
						k += 5;
						memcpy(longname + k, dir + i - j + 14, 12);
						k += 6;
						memcpy(longname + k, dir + i - j + 28, 4);
						k += 2;
					/* while the six's bit of the long name entry isn't set to 1 */
					} while (!(dir[i - j] & 0x40));
					for (j = 0; j < sizeof(longname); ++j) {
						biggest_file.longname[j] = (unsigned char) longname[j];
					}
				}
				memcpy(&biggest_file.name, entry, 11);
				biggest_file.first_cluster = dir_cluster;
				/*memcpy(&biggest_file.first_cluster, entry + 0x14, 2);*/
				/*memcpy(&biggest_file.first_cluster + 2, entry + 0x1A, 2);*/
				memcpy(&biggest_file.size, entry + 0x1C, 4);
			}
		}
	}
	return biggest_file;
}


int main(int argc, char *argv[])
{
	int i, j;
	unsigned char buf[512];

	if (argc != 3) {
		fprintf(stderr, "usage: %s disk partition_number\n", argv[0]);
		return -1;
	}

	if ((f = fopen(argv[1], "rb")) == NULL) {
		fprintf(stderr, "error: cannot open the file\n");
		return -1;
	}


	/* READING THE MBR */

	if (fread(buf, 512, 1, f) > 0) {
		/* sanity check */
		if (buf[510] != 0x55 || buf[511] != 0xAA) {
			fprintf(stderr, "error: the MBR is not correct\n");
			return -1;
		}
	}
	else {
		fprintf(stderr, "could not read the disk\n");
		return -1;
	}
	printf("done reading MBR\n");

	/* checking type code */
	i = buf[446 + (atoi(argv[2]) - 1) * 16 + 4];
	if (i != 0x0B && i != 0x0C) {
		fprintf(stderr, "error: not a FAT32 partition\n");
		return -1;
	}

	/* i is set at the LBA Begin bytes position */
	i = 446 + (atoi(argv[2]) - 1) * 16 + 8;
	/* reading LBA Begin (4 bytes) */
	lba_begin = 0;
	lba_begin = 0;
	memcpy(&lba_begin, buf + i, 4);
	printf("FAT volume id sector: %d\n", lba_begin);

	/* i is set at the sector number info bytes position */
	i = 446 + (atoi(argv[2]) - 1) * 16 + 12;
	/* reading number of sectors (4 bytes) */
	number_of_sectors = 0;
	memcpy(&number_of_sectors, buf + i, 4);
	printf("%d sectors or %d Mb\n", number_of_sectors, number_of_sectors / 2 / 1024);

	/* DONE READING THE MBR */


	/* READING FAT VOLUME ID */

	fseek(f, lba_begin * 512, SEEK_SET);
	if (fread(buf, 512, 1, f) > 0) {
		/* sanity check */
		if (buf[510] != 0x55 || buf[511] != 0xAA) {
			fprintf(stderr, "error: the FAT table is not correct\n");
			return -1;
		}
	}
	else {
		fprintf(stderr, "could not read the disk\n");
		return -1;
	}

	bytes_per_sector = 0;
	memcpy(&bytes_per_sector, buf + 0x0B, 2);
	printf("bytes per sector: %d\n", bytes_per_sector);

	sectors_per_cluster = buf[0x0D];
	printf("sectors per cluster: %d\n", sectors_per_cluster);

	number_of_reserved_sectors = 0;
	memcpy(&number_of_reserved_sectors, buf + 0x0E, 2);
	printf("reserved %x sectors\n", number_of_reserved_sectors);

	number_of_fats = buf[0x10];
	if (number_of_fats != 2)
		fprintf(stderr, "warning: partition has not 2 FAT tables\n");
	else
		printf("partition has %d FAT tables\n", number_of_fats);

	sectors_per_fat = 0;
	memcpy(&sectors_per_fat, buf + 0x24, 4);
	printf("sectors per fat: %d\n", sectors_per_fat);

	root_dir_first_cluster = 0;
	memcpy(&root_dir_first_cluster, buf + 0x2C, 4);
	printf("root dir first cluster: %d\n", root_dir_first_cluster);

	fat_begin_lba = lba_begin + number_of_reserved_sectors;

	printf("FAT table begin lba: %d\n", fat_begin_lba);
	printf("FAT table begin address: 0x%x\n", fat_begin_lba * bytes_per_sector);

	cluster_begin_lba = lba_begin + number_of_reserved_sectors + number_of_fats * sectors_per_fat;
	printf("clusters begin lba %d\n", cluster_begin_lba);

	/* DONE READING FAT VOLUME ID */


	struct file result;
	result = find_biggest(root_dir_first_cluster);

	/*printf("biggest file name: %s\n", (result.longname[0] == 0) ? result.name : result.longname);*/
	printf("biggest file name: %s\n", result.longname);
	/*fwprintf(f1, L"biggest file name: %s\n", result.longname);*/
	printf("biggest file size: %lu bytes\n", result.size);
	printf("%u\n",(unsigned) result.first_cluster);

	printf("done\n");

	return 0;
}
